/*
 * COPYRIGHT:	2010 Christopher Smith, licenced under the GNU GPL v2; see COPYING in the top level directory
 * PROJECT:		sdedit
 * FILE:		lsa module.cpp
 * PURPOSE:		export of module resources and types
 */

namespace Lsa
{

class LsaModule : public Module::ModuleObject
{
	auto_SDE_OPTS ModOpts;

public:
	LsaModule() :
		ModOpts(StaticLsaOpts)
	{}

	PCWSTR ModuleName() override
	{
		return L"LSA";
	}

	virtual SDE_OPTS &ModuleOptions() override
	{
		return ModOpts;
	}

private:
	void AddResourcesFromPolicy(LsaPolicy *Policy, Module::ModuleResourceDest &Res)
	{
		auto_refct<Lookup::Translator<SID> > SidTrans = new SidTranslator(Policy);
		Res.SidTranslator(L"LSA", SidTrans);

		auto_refct<Lookup::DomainEnumerator> DomainEnum = new DomainEnumerator(Policy);
		Res.DomainEnumerator(L"LSA", DomainEnum);

		if(ModOpts.Opts[OiEnumEnabled].Bool) //due to the low quality results of LSA accout enumeration, it has to be enabled explicitly
		{
			auto_refct<Lookup::DomainAccountEnumerator> DomainAcctEnum = new DomainAccountEnumerator(ModOpts.Opts[OiSidEnumTypes].Enum->Choice32);
			Res.DomainSidEnumerator(DomainAcctEnum);
		}
	}

	static int ExceptionToOpenStatus(EXCEPTION_POINTERS *ExceptionPointers, Module::OPEN_STATUS &OpenStatus, BOOLEAN ObjectWanted)
	{
		if(ExceptionPointers->ExceptionRecord->ExceptionCode == STATUS_NT_ERROR)
			switch(CodeFromExceptionRecord(ExceptionPointers->ExceptionRecord))
			{
			case STATUS_ACCESS_DENIED:
				OpenStatus |= Module::SECURITY_FAILURE;
				if(ObjectWanted)
					OpenStatus |= Module::OBJECT_OPEN_FAIL;
				break;

			case STATUS_OBJECT_NAME_NOT_FOUND:
			case STATUS_NO_SUCH_USER:
				OpenStatus |= Module::OBJECT_NAME_NOTFOUND;
				break;

			case STATUS_OBJECT_NAME_INVALID:
			case STATUS_OBJECT_PATH_INVALID:
			case STATUS_INVALID_ACCOUNT_NAME:
			case STATUS_INVALID_COMPUTER_NAME:
				OpenStatus |= Module::OBJECT_NAME_INVALID;
				break;
			}

		OpenStatus |= Module::COMPLETE_FAILURE;

		return EXCEPTION_CONTINUE_SEARCH; //just passing through
	}

	void OpenResourcesGuarded(Module::OPEN_STATUS &OpenStatus, SDE_GENERIC_OPTS const &GOpts, Module::ModuleResourceDest &Res, UCHAR Privileges, BOOLEAN ObjectWanted)
	{
		auto_refct<LsaPolicy> LsaPolicy;

		if(ObjectWanted)
		{
			PCWSTR Name = GOpts.ObjName;
			PCWSTR Type = GOpts.ObjType;

			LsaPolicy = new Lsa::LsaPolicy(GOpts, ModOpts, Privileges);

			if(!Type)
			{
				OpenStatus |= Module::TYPE_NOT_SUPPORTED;
				return;
			}
			else if(_wcsicmp(Type, L"Policy") == 0)
				Res.Object(new Policy(LsaPolicy));
			else if(_wcsicmp(Type, L"Account") == 0)
				Res.Object(Account::CreateNewByTextSid(LsaPolicy, Name, Privileges));
			else if(_wcsicmp(Type, L"AccountByName") == 0)
				Res.Object(Account::CreateNewByName(LsaPolicy, Name, Privileges));
			else if(_wcsicmp(Type, L"Secret") == 0)
				Res.Object(Secret::CreateNew(LsaPolicy, Name, Privileges));
			else
			{
				OpenStatus |= Module::TYPE_NOT_SUPPORTED;
				return;
			}

			OpenStatus |= Module::OBJECT_OPENED;
		}
		else
		{
			LsaPolicy = new Lsa::LsaPolicy(GOpts, ModOpts, 0);
		}
		
		AddResourcesFromPolicy(LsaPolicy, Res);
	}

public:
	virtual void OpenResources(Module::OPEN_STATUS &OpenStatus, SDE_GENERIC_OPTS const &GOpts, Module::ModuleResourceDest &Res, UCHAR Privileges, BOOLEAN ObjectWanted) override
	{
		__try
		{
			OpenResourcesGuarded(OpenStatus, GOpts, Res, Privileges, ObjectWanted);
		}
		__except(ExceptionToOpenStatus(exception_info(), OpenStatus, ObjectWanted))
		{}
	}
};

}//namespace Lsa